package com.android.superli.btremote.hid;

import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HidConsts {

    public final static String NAME = "app_remote";
    public final static String DESCRIPTION = "description";
    public final static String PROVIDER = "provider";
    public static BluetoothHidDevice HidDevice;
    public static BluetoothDevice BtDevice;
    public static byte ModifierByte = 0x00;
    public static byte KeyByte = 0x00;

    public final static byte[] Descriptor = {
            // 鼠标
            (byte) 0x05, (byte) 0x01,   // USAGE_PAGE(Generic Desktop)
            (byte) 0x09, (byte) 0x02,   // USAGE(Mouse)
            (byte) 0xa1, (byte) 0x01,   // COLLECTION(Application)
            (byte) 0x09, (byte) 0x01,   // USAGE(Pointer)
            (byte) 0xa1, (byte) 0x00,   // COLLECTION(Physical)
            (byte) 0x85, (byte) 0x01,   // REPORT ID(1)
            (byte) 0x05, (byte) 0x09,   // USAGE PAGE(Button)
            (byte) 0x19, (byte) 0x01,   // USAGE_MINIMUM(Button1)
            (byte) 0x29, (byte) 0x03,   // USAGE_MAXIMUM(Button3)
            (byte) 0x15, (byte) 0x00,   // LOGICAL_MINIMUM(0)
            (byte) 0x25, (byte) 0x01,   // LOGICAL_MAXIMUM(1)
            (byte) 0x95, (byte) 0x03,   // REPORT_COUNT(3)
            (byte) 0x75, (byte) 0x01,   // REPORT_SIZE(1)
            (byte) 0x81, (byte) 0x02,   // INPUT(Data,Var,Abs)
            (byte) 0x95, (byte) 0x01,   // REPORT_COUNT(1)
            (byte) 0x75, (byte) 0x05,   // REPORT_SIZE(5)
            (byte) 0x81, (byte) 0x03,   // INPUT(Cnst,Var,Abs)
            (byte) 0x05, (byte) 0x01,   // USAGE_PAGE(Generic Desktop)
            (byte) 0x09, (byte) 0x30,   // USAGE(X)
            (byte) 0x09, (byte) 0x31,   // USAGE(Y)
            (byte) 0x09, (byte) 0x38,   // USAGE(Wheel)
            (byte) 0x15, (byte) 0x81,   // LOGICAL_MINIMUM(-127)
            (byte) 0x25, (byte) 0x7f,   // LOGICAL_MAXIMUM(127)
            (byte) 0x75, (byte) 0x08,   // REPORT_SIZE(8)
            (byte) 0x95, (byte) 0x03,   // REPORT_COUNT(3)
            (byte) 0x81, (byte) 0x06,   // INPUT(Data,Var,Rel)
            (byte) 0xc0,                // END_COLLECTION
            (byte) 0xc0,                // END_COLLECTION
            // 键盘
            (byte) 0x05, (byte) 0x01,   // USAGE_PAGE(Generic Desktop)
            (byte) 0x09, (byte) 0x06,   // USAGE(Keyboard)
            (byte) 0xa1, (byte) 0x01,   // COLLECTION(Application)
            (byte) 0x85, (byte) 0x02,   // REPORT ID(2)
            (byte) 0x05, (byte) 0x07,   // USAGE_PAGE(Keyboard)
            (byte) 0x19, (byte) 0xE0,   // USAGE_MINIMUM(LeftControl)
            (byte) 0x29, (byte) 0xE7,   // USAGE_MAXIMUM(RightGUI)
            (byte) 0x15, (byte) 0x00,   // LOGICAL_MINIMUM(0)
            (byte) 0x25, (byte) 0x01,   // LOGICAL_MAXIMUM(1)
            (byte) 0x75, (byte) 0x01,   // REPORT_SIZE(1)
            (byte) 0x95, (byte) 0x08,   // REPORT_COUNT(8)
            (byte) 0x81, (byte) 0x02,   // INPUT(Data,Var,Abs)
            (byte) 0x75, (byte) 0x10,   // REPORT_SIZE(8)
            (byte) 0x95, (byte) 0x01,   // REPORT_COUNT(1)
            (byte) 0x15, (byte) 0x00,   // LOGICAL_MINIMUM(0)
            (byte) 0x26, (byte) 0xFE, 0x02, // LOGICAL_MINIMUM(0)
            (byte) 0x19, (byte) 0x00,   // USAGE_MINIMUM(0)
            (byte) 0x2A, (byte) 0xFE, 0x02, // USAGE_MAXIMUM(101)
//            (byte) 0x0A, (byte) 0xCA, 0x02,  // USAGE_MINIMUM(0)
            (byte) 0x81, (byte) 0x00,   // INPUT(Data,Ary,Abs)

//            (byte) 0x05, (byte) 0x01,   // Usage Page (Generic Desktop) --
//            (byte) 0x09, (byte) 0x81,   // 关机键 --
//            (byte) 0x95, (byte) 0x01,   // REPORT_COUNT(1) --
//            (byte) 0x75, (byte) 0x08,   // REPORT_SIZE(1) --
//            (byte) 0x15, (byte) 0x00,   // LOGICAL_MINIMUM(-127) --
//            (byte) 0x25, (byte) 0x01,   // LOGICAL_MAXIMUM(127) --
//            (byte) 0x81, (byte) 0x02,   // INPUT(Data,Var,Rel) --

            (byte) 0x05, (byte) 0x08,   // USAGE_PAGE(LEDs)
            (byte) 0x95, (byte) 0x05,   // REPORT_COUNT(5)
            (byte) 0x75, (byte) 0x01,   // REPORT_SIZE(1)
            (byte) 0x19, (byte) 0x01,   // USAGE_MINIMUM(1)
            (byte) 0x29, (byte) 0x05,   // USAGE_MAXIMUM(5)
            (byte) 0x91, (byte) 0x02,   // OUTPUT(Data,Var,Abs)
            (byte) 0x95, (byte) 0x01,   // REPORT_COUNT(1)
            (byte) 0x75, (byte) 0x03,   // REPORT_SIZE(3)
            (byte) 0x91, (byte) 0x03,   // OUTPUT(Cnst,Var,Abs)
            (byte) 0xc0,                // END_COLLECTION

            (byte) 0x05, (byte) 0x0c,   // Usage Page (Generic Desktop)
            (byte) 0x09, (byte) 0x01,   // Usage (Sys Control)
            (byte) 0xa1, (byte) 0x01,   // Collection (Application)
            (byte) 0x85, (byte) 0x03,   // REPORT ID(2)
            (byte) 0x09, (byte) 0xea,         /* USAGE (Volume Decrement) *///區域性：用途：音量減
            (byte) 0x09, (byte) 0xe9,         /* USAGE (Volume Increment) *///區域性：用途：音量加
            (byte) 0x09, (byte) 0xe2,         /* USAGE (Mute) *///區域性：用途：靜音
            (byte) 0x09, (byte) 0xcd,         /* USAGE (Play/Pause) *///區域性：用途：播放暫停
            (byte) 0x0a, (byte) 0x23, (byte) 0x02,   /* USAGE (AC Home) *///區域性：用途：開啓瀏覽器0x0223
            (byte) 0x15, (byte) 0x00,         /* LOGICAL_MIN *///全域性：返回邏輯最小值：0
            (byte) 0x25, (byte) 0x01,         /* LOGICAL_MAX *///全域性：返回邏輯最大值：1
            (byte) 0x95, (byte) 0x05,         /* REPORT_COUNT *///全域性：數據域數量
            (byte) 0x75, (byte) 0x01,         /* REPORT_SIZE *///全域性：一個數據域的位長度
            (byte) 0x81, (byte) 0x02,         /* INPUT(Data,Var,Abs) *///主條目：說明有5個長度爲1的數據域
            (byte) 0x05, (byte) 0x01,         /* USAGE_PAGE (Generic Desktop) *///全域性：用途頁：桌面
            (byte) 0x09, (byte) 0x82,         /* USAGE（System Sleep）*///區域性：用途：系統待機
            (byte) 0x09, (byte) 0x81,         /* USAGE（System Power down）*///區域性：用途：系統關機
            (byte) 0x95, (byte) 0x02,         /* REPORT_COUNT *///全域性：數據域數量
            (byte) 0x75, (byte) 0x01,         /* REPORT_SIZE *///全域性：一個數據域的位長度
            (byte) 0x81, (byte) 0x02,         /* INPUT(Data,Var,Abs) *///主條目：說明有2個長度爲1的數據域
            (byte) 0x95, (byte) 0x01,         /* REPORT_COUNT *///全域性：數據域數量
            (byte) 0x75, (byte) 0x01,         /* REPORT_SIZE *///全域性：一個數據域的位長度
            (byte) 0x81, (byte) 0x03,         /* INPUT(Cnst,Var,Abs) *///主條目：說明有1個長度爲1的常數
            (byte) 0xc0                /* END_COLLECTION *///主條目：關集合
    };

    private static Handler handler;
    private static ExecutorService singleThreadExecutor;

    public static void reporttrans() {
        singleThreadExecutor = Executors.newSingleThreadExecutor();
        singleThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler = new Handler(Looper.myLooper()) {

                    @Override
                    public void handleMessage(@NonNull Message msg) {
                        super.handleMessage(msg);
                        HidReport mHidReport = (HidReport) msg.obj;
                        postReport(mHidReport);
                    }
                };
                Looper.loop();
            }
        });
    }

    private static void postReport(HidReport report) {
        report.SendState = HidReport.State.Sending;
        Log.e("postReport", "ID:" + report.ReportId + "\t\tDATA:" + BytesUtils.toHexStringForLog(report.ReportData));
        boolean ret = HidDevice.sendReport(BtDevice, report.ReportId, report.ReportData);
        if (!ret) {
            report.SendState = HidReport.State.Failded;
        } else {
            report.SendState = HidReport.State.Sended;
        }
    }

    public static void exit() {
        if (handler != null) {
            handler.getLooper().quit();
            handler = null;
        }

        if (singleThreadExecutor != null && !singleThreadExecutor.isShutdown()) {
            singleThreadExecutor.shutdown();
            singleThreadExecutor = null;
        }
    }

    public static void CleanKbd() {
        SendKeyReport(new byte[]{0, 0});
    }

    protected static void addInputReport(final HidReport inputReport) {
        if (handler == null || singleThreadExecutor == null) {
            reporttrans();
        }
        if (inputReport != null && handler != null) {
            Message msg = new Message();
            msg.obj = inputReport;
            handler.sendMessage(msg);
        }
    }

    public static void SendMouseReport(byte[] reportData) {
        HidReport report = new HidReport(HidReport.DeviceType.Mouse, (byte) 0x01, reportData);
        addInputReport(report);
    }

    private static HidReport MouseReport = new HidReport(HidReport.DeviceType.Mouse, (byte) 0x01, new byte[]{0, 0, 0, 0});

    public static void MouseMove(int dx, int dy, int wheel, final boolean leftButton, final boolean rightButton, final boolean middleButton) {

        if (MouseReport.SendState.equals(HidReport.State.Sending)) {
            return;
        }
        if (dx > 127) dx = 127;
        if (dx < -127) dx = -127;
        if (dy > 127) dy = 127;
        if (dy < -127) dy = -127;
        if (wheel > 127) wheel = 127;
        if (wheel < -127) wheel = -127;
        if (leftButton) {
            MouseReport.ReportData[0] |= 1;
        } else {
            MouseReport.ReportData[0] = (byte) (MouseReport.ReportData[0] & (~1));
        }
        if (rightButton) {
            MouseReport.ReportData[0] |= 2;
        } else {
            MouseReport.ReportData[0] = (byte) (MouseReport.ReportData[0] & (~2));
        }
        if (middleButton) {
            MouseReport.ReportData[0] |= 4;
        } else {
            MouseReport.ReportData[0] = (byte) (MouseReport.ReportData[0] & (~4));
        }
        MouseReport.ReportData[1] = (byte) dx;
        MouseReport.ReportData[2] = (byte) dy;
        MouseReport.ReportData[3] = (byte) wheel;

        addInputReport(MouseReport);
    }

    public static void LeftBtnDown() {
        HidConsts.MouseReport.ReportData[0] |= 1;
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static void LeftBtnUp() {
        HidConsts.MouseReport.ReportData[0] &= (~1);
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static void LeftBtnClick() {
        LeftBtnDown();
        UtilCls.DelayTask(new Runnable() {
            @Override
            public void run() {
                LeftBtnUp();
            }
        }, 20, true);
    }

    public static TimerTask LeftBtnClickAsync(int delay) {
        return UtilCls.DelayTask(new Runnable() {
            @Override
            public void run() {
                LeftBtnClick();
            }
        }, delay, true);
    }

    public static void RightBtnDown() {
        HidConsts.MouseReport.ReportData[0] |= 2;
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static void RightBtnUp() {
        HidConsts.MouseReport.ReportData[0] &= (~2);
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static void MidBtnDown() {
        HidConsts.MouseReport.ReportData[0] |= 4;
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static void MidBtnUp() {
        HidConsts.MouseReport.ReportData[0] &= (~4);
        SendMouseReport(HidConsts.MouseReport.ReportData);
    }

    public static byte ModifierDown(byte UsageId) {
        synchronized (HidConsts.class) {
            ModifierByte |= UsageId;
        }
        return ModifierByte;
    }

    public static byte ModifierUp(byte UsageId) {
        UsageId = (byte) (~((byte) (UsageId)));
        synchronized (HidConsts.class) {
            ModifierByte = (byte) (ModifierByte & UsageId);
        }
        return ModifierByte;
    }

    public static void KbdKeyDown(String usageStr) {
        if (!TextUtils.isEmpty(usageStr)) {
            if (usageStr.startsWith("M")) {
                usageStr = usageStr.replace("M", "");
                synchronized (HidConsts.class) {
                    byte mod = ModifierDown((byte) Integer.parseInt(usageStr));
                    SendKeyReport(new byte[]{mod, KeyByte});
                }
            } else if (TextUtils.equals(usageStr, "0x0223")) {
                SendConsumerKeyReport(new byte[]{1 << 4});
            } else if (TextUtils.equals(usageStr, "0x81")) {
                int deviceClass = BtDevice.getBluetoothClass().getMajorDeviceClass();
                if (deviceClass == BluetoothClass.Device.Major.COMPUTER) {
                    powerKeyDown();
                } else {
                    byte key = 102;
                    synchronized (HidConsts.class) {
                        KeyByte = key;
                        SendKeyReport(new byte[]{ModifierByte, KeyByte});
                    }
                }
            } else if (TextUtils.equals(usageStr, "0x40")) {
                // 返回键，发送鼠标右键
                RightBtnDown();
            } else if (usageStr.startsWith("71")) {
                int value = Integer.parseInt(usageStr) + 0xC;
                byte bit1 = (byte) (value & 0xFF);
                byte bit2 = (byte) ((value >>> 8) & 0xFF);
                SendKeyReport(new byte[]{ModifierByte, bit1, bit2});
            } else {
                byte key = (byte) Integer.parseInt(usageStr);
                synchronized (HidConsts.class) {
                    KeyByte = key;
                    SendKeyReport(new byte[]{ModifierByte, KeyByte});
                }
            }
        }
    }

    public static void KbdKeyUp(String usageStr) {
        if (!TextUtils.isEmpty(usageStr)) {
            if (usageStr.startsWith("M")) {
                usageStr = usageStr.replace("M", "");
                synchronized (HidConsts.class) {
                    byte mod = ModifierUp((byte) Integer.parseInt(usageStr));
                    SendKeyReport(new byte[]{mod, KeyByte});
                }
            } else if (TextUtils.equals(usageStr, "0x0223")) {
                SendConsumerKeyReport(new byte[]{0});
            } else if (TextUtils.equals(usageStr, "0x81")) {
                int deviceClass = BtDevice.getBluetoothClass().getMajorDeviceClass();
                if (deviceClass == BluetoothClass.Device.Major.COMPUTER) {
                    powerKeyUp();
                } else {
                    synchronized (HidConsts.class) {
                        KeyByte = 0;
                        SendKeyReport(new byte[]{ModifierByte, KeyByte});
                    }
                }
            } else if (TextUtils.equals(usageStr, "0x40")) {
                // 返回键，发送鼠标右键
                RightBtnUp();
            } else {
                synchronized (HidConsts.class) {
                    KeyByte = 0;
                    SendKeyReport(new byte[]{ModifierByte, KeyByte, 0x0});
                }
            }
        }
    }

    public static void powerKeyDown() {
        SendConsumerKeyReport(new byte[]{0x0});
    }

    public static void powerKeyUp() {
        SendConsumerKeyReport(new byte[]{0x1 << 6});
    }

    public static void SendKeyReport(byte[] reportData) {
        HidReport report = new HidReport(HidReport.DeviceType.Keyboard, (byte) 0x02, reportData);
        addInputReport(report);
    }

    public static void SendConsumerKeyReport(byte[] reportData) {
        HidReport report = new HidReport(HidReport.DeviceType.Consumer, (byte) 0x03, reportData);
        addInputReport(report);
    }
}
